home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / clang / nn.zip / NNTP.C < prev    next >
C/C++ Source or Header  |  1989-12-31  |  18KB  |  771 lines

  1. /* 
  2.  * nntp module for nn.
  3.  *
  4.  * The original taken from the nntp 1.5 clientlib.c
  5.  * Modified heavily for nn.
  6.  *
  7.  * Rene' Seindal (seindal@diku.dk) Thu Dec  1 18:41:23 1988
  8.  *
  9.  * Last change: Fri Jul  7 18:56:00 1989
  10.  */
  11.  
  12.  
  13. #include "config.h"
  14.  
  15. /* 
  16.  *     nn maintains a cache of recently used articles to improve efficiency.
  17.  *     To change the size of the cache, define NNTPCACHE in config.h to be
  18.  *    the new size of this cache.
  19.  */
  20.  
  21. #ifndef NNTPCACHE
  22. #define NNTPCACHE    10
  23. #endif
  24.  
  25. #ifdef NNTP
  26. #include <stdio.h>
  27. #include "nntp.h"
  28. #include <sys/types.h>
  29. #include <sys/socket.h>
  30. #include <netdb.h>
  31.  
  32. /* This is necessary due to the definitions in m-XXX.h */
  33. #if !defined(NETWORK_DATABASE) || defined(NETWORK_BYTE_ORDER)
  34. #include <netinet/in.h>
  35. #endif
  36.  
  37. export char nntp_server[256];    /* name of nntp server */
  38. export int use_nntp = 0;    /* bool: t iff we use nntp */
  39. export int nntp_failed = 0;    /* bool: t iff connection is broken
  40.                    in nntp_get_article() or nntp_get_active() */
  41.  
  42. import int silent;
  43. import char news_active[];
  44.  
  45. import int errno, sys_nerr;
  46. import char *sys_errlist[];
  47. extern int user_error();
  48. extern int sys_error();
  49.  
  50. #define syserr() (errno >= 0 && errno < sys_nerr ? \
  51.           sys_errlist[errno] : "Unknown error.")
  52.  
  53. import char *mktemp();
  54.  
  55. static FILE *nntp_in = NULL;        /* fp for reading from server */
  56. static FILE *nntp_out = NULL;        /* fp for writing to server */
  57. static int is_connected = 0;        /* bool: t iff we are connected */
  58. static group_header *group_hd;        /* ptr to servers current group */
  59. static int is_set = 0;            /* bool: t iff group_hd is set */
  60. static int try_again = 0;        /* bool: t if timeout forces retry */
  61. static int can_post = 0;        /* bool: t iff NNTP server accepts postings */
  62.  
  63. #define ERR_TIMEOUT    503        /* Response code for timeout */
  64.  
  65. #ifdef DEBUG
  66. static FILE *nntp_debug;
  67.  
  68. /* 
  69.  * nntp_debug_msg: print a debug message.
  70.  *
  71.  *    The master appends prefix and str to a log file, and clients
  72.  *    prints it as a message.
  73.  */
  74.  
  75. nntp_debug_msg(prefix, str)
  76.     char *prefix, *str;
  77. {
  78.     if (is_master) {
  79.     if (nntp_debug != 0) {
  80.         fprintf(nntp_debug, "%s %s\n", prefix, str);
  81.         fflush(nntp_debug);
  82.     }
  83.     } else {
  84.     msg("%s %s", prefix, str);
  85.     fl;
  86.     sleep();
  87.     }
  88. }
  89. #endif
  90.  
  91. /* 
  92.  * nntp_find_server: Find out which host to use as NNTP server.
  93.  *
  94.  *     This is done by consulting the file NNTP_SERVER (defined in
  95.  *     config.h).  Set nntp_server[] to the host's name.
  96.  */
  97.  
  98. void nntp_find_server()
  99. {
  100.     char *cp, *name, *getenv();
  101.     char buf[BUFSIZ];
  102.     FILE *fp;
  103.     
  104.     /* 
  105.      * This feature cannot normally be enabled, because the database and
  106.      * the users rc file contains references to articles by number, and
  107.      * these numbers are not unique across NNTP servers.
  108.      */
  109. #ifdef DEBUG 
  110.     if ((cp = getenv("NNTPSERVER")) != NULL) {
  111.     strncpy(nntp_server, cp, sizeof nntp_server);
  112.     return;
  113.     }
  114. #endif /* DEBUG */
  115.     
  116.     name = NNTP_SERVER;
  117.     if (*name != '/')
  118.     name = relative(lib_directory, name);
  119.     
  120.     if ((fp = open_file(name, OPEN_READ)) != NULL) {
  121.     while (fgets(buf, sizeof buf, fp) != 0) {
  122.         if (*buf == '#' || *buf == '\n')
  123.         continue;
  124.         if ((cp = strchr(buf, '\n')) != 0)
  125.         *cp = '\0';
  126.         strncpy(nntp_server, buf, sizeof nntp_server);
  127.         fclose(fp);
  128.         return;
  129.     }
  130.     fclose(fp);
  131.     }
  132.     
  133.     if (!is_master)
  134.     printf("\nCannot find name of NNTP server.\nCheck %s\n", name);
  135.     
  136.     sys_error("Failed to find name of NNTP server!");
  137. }
  138.  
  139. /* 
  140.  * nntp_check: Find out whether we need to use NNTP.
  141.  *
  142.  *     This is done by comparing the NNTP servers name with whatever
  143.  *     gethostname() returns.  
  144.  *    use_nntp and news_active[] are initialised as a side effect.
  145.  */
  146.  
  147. void nntp_check()
  148. {
  149.     char host[128];
  150.     char *cp;
  151.     
  152.     nntp_find_server();
  153.     gethostname(host, sizeof host);
  154.     use_nntp = strcmp(host, nntp_server) != 0; /* too simplistic ??? */
  155.     
  156.     if (use_nntp) 
  157.     strcpy(news_active, relative(db_directory, "ACTIVE"));
  158.     else 
  159.     strcpy(news_active, NEWS_ACTIVE);
  160. }
  161.  
  162. /* 
  163.  * nntp_server_init: initialise a connection to the nntp server.
  164.  *
  165.  *     It expects nntp_server[] to be set previously, by a call to
  166.  *     nntp_check.  It is called from nntp_get_article() and 
  167.  *    nntp_get_active() if there is no established connection.
  168.  */
  169.  
  170. nntp_server_init()
  171. {
  172.     int sockt_rd, sockt_wr;
  173.     int response;
  174.     char line[NNTP_STRLEN];
  175.     
  176.     if (!is_master && !silent)
  177.     msg("Connecting to NNTP server %s ... ", nntp_server);
  178.     
  179.     nntp_failed = 1;
  180.     is_connected = 0;
  181.  
  182. #ifdef DEBUG
  183.     if (is_master) {
  184.     if (nntp_debug != 0) {
  185.         fclose(nntp_debug);
  186.         rename("/usr/tmp/nnmaster.log", "/usr/tmp/nnmaster.prev");
  187.     }
  188.     nntp_debug = fopen("/usr/tmp/nnmaster.log", "a");
  189.     } else
  190.     nntp_debug = 0;
  191. #endif
  192.     
  193.     sockt_rd = nntp_get_socket();
  194.     if (sockt_rd < 0)
  195.     return -1;
  196.     
  197.     if ((nntp_in = fdopen(sockt_rd, "r")) == NULL) {
  198.     close(sockt_rd);
  199.         return -1;
  200.     }
  201.     sockt_wr = dup(sockt_rd);
  202.     if ((nntp_out = fdopen(sockt_wr, "w")) == NULL) {
  203.     close(sockt_wr);
  204.     fclose(nntp_in);
  205.         nntp_in = NULL;               /* from above */
  206.         return -1;
  207.     }
  208.     
  209.     /* Now get the server's signon message */
  210.     response = nntp_get_server(line, sizeof(line));
  211.     
  212.     if (is_master) {
  213.     if (response != OK_CANPOST && response != OK_NOPOST) {
  214.         log_entry('N', "Failed to connect to NNTP server");
  215.         log_entry('N', "Response: %s", line);
  216.         fclose(nntp_out);
  217.         fclose(nntp_in);
  218.         return -1;
  219.     }
  220.     } else {
  221.     switch (response) {
  222.     case OK_CANPOST:
  223.         can_post = 1;
  224.         break;
  225.     case OK_NOPOST:
  226.         can_post = 0;
  227.         break;
  228.     default:
  229.         user_error(line);
  230.         /* NOTREACHED */
  231.     }
  232.     }
  233.     if (!is_master && !silent)
  234.     msg("Connecting to NNTP server %s ... ok (%s)",
  235.         nntp_server, can_post ? "posting is allowed" : "no posting");
  236.     
  237.     is_connected = 1;
  238.     is_set = 0;
  239.     nntp_failed = 0;
  240.     try_again = 0;
  241.     return 0;
  242. }
  243.  
  244. /* 
  245.  * nntp_get_socket:  get a connection to the nntp server.
  246.  *
  247.  *     Doesn't return in case of errors.
  248.  */
  249.  
  250. nntp_get_socket()
  251. {
  252.     int s;
  253.     struct sockaddr_in sin;
  254.     struct servent *getservbyname(), *sp;
  255.     struct hostent *gethostbyname(), *hp;
  256. #ifdef h_addr
  257.     int     x = 0;
  258.     register char **cp;
  259. #endif
  260.     int (*errfct)() = is_master ? sys_error : user_error;
  261.     
  262.     if ((sp = getservbyname("nntp", "tcp")) ==  NULL)
  263.     (*errfct)("nntp/tcp: Unknown service.\n");
  264.     
  265.     if ((hp = gethostbyname(nntp_server)) == NULL)
  266.     (*errfct)("NNTP server %s unknown.\n", nntp_server);
  267.     
  268.     bzero((char *) &sin, sizeof(sin));
  269.     sin.sin_family = hp->h_addrtype;
  270.     sin.sin_port = sp->s_port;
  271.     
  272. #ifdef  h_addr
  273.     /* get a socket and initiate connection -- use multiple addresses */
  274.     
  275.     for (cp = hp->h_addr_list; cp && *cp; cp++) {
  276.     s = socket(hp->h_addrtype, SOCK_STREAM, 0);
  277.     if (s < 0)
  278.         (*errfct)("Can't get NNTP socket: %s\n", syserr());
  279.     bcopy(*cp, (char *)&sin.sin_addr, hp->h_length);
  280.     
  281.     x = connect(s, (struct sockaddr *)&sin, sizeof (sin));
  282.     if (x == 0)
  283.         break;
  284.     if (!is_master)
  285.         msg("Connecting to %s failed: %s", nntp_server, syserr());
  286.     (void) close(s);
  287.     s = -1;
  288.     }
  289.     if (x < 0 && !is_master)
  290.     (*errfct)("Giving up on NNTP server %s!\n", nntp_server);
  291. #else                    /* no name server */
  292.     if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  293.     (*errfct)("Can't get NNTP socket: %s\n", syserr());
  294.     
  295.     /* And then connect */
  296.     bcopy(hp->h_addr, (char *) &sin.sin_addr, hp->h_length);
  297.     if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
  298.     if (is_master)
  299.         (*errfct)("Connecting to %s failed: %s", nntp_server, syserr());
  300.     s = -1;
  301.     }
  302. #endif
  303.     return s;
  304. }
  305.  
  306. /* 
  307.  * nntp_put_server:  send a line to the nntp server.
  308.  *
  309.  *     Expects to be connected to the server.  
  310.  */
  311.  
  312. nntp_put_server(string)
  313.     char *string;
  314. {
  315. #ifdef DEBUG
  316.     nntp_debug_msg(">>>", string);
  317. #endif
  318.     fprintf(nntp_out, "%s\r\n", string);
  319.     if (fflush(nntp_out) == EOF) {
  320.     nntp_io_error();
  321.     return -1;
  322.     }
  323.     return 0;
  324. }
  325.  
  326. /* 
  327.  * nntp_get_server_line: get a line from the server.
  328.  *
  329.  *     Expects to be connected to the server.
  330.  *     The line can be any kind of line, i.e., either response or text.
  331.  */
  332.  
  333. nntp_get_server_line(string, size)
  334.     char *string;
  335.     int size;
  336. {
  337.     register char *cp, *nl;
  338.     
  339.     if (fgets(string, size, nntp_in) == NULL) {
  340.     nntp_io_error();
  341.     return -1;
  342.     }
  343.     for (cp = string, nl = NULL; *cp != NUL; cp++) {
  344.     if (*cp == CR) {
  345.         nl = cp;
  346.         break;
  347.     }
  348.     if (nl == NULL && *cp == NL) 
  349.         nl = cp;
  350.     }    
  351.     if (nl != NULL) *nl = NUL;
  352.     
  353.     return 0;
  354. }
  355.  
  356. /* 
  357.  * nntp_get_server: get a response line from the server.
  358.  *
  359.  *     Expects to be connected to the server.  
  360.  *     Returns the numerical value of the reponse, or -1 in case of errors.
  361.  */
  362.  
  363. nntp_get_server(string, size)
  364.     char *string;
  365.     int size;
  366. {
  367.     if (nntp_get_server_line(string, size) < 0)
  368.     return -1;
  369. #ifdef DEBUG
  370.     nntp_debug_msg("<<<", string);
  371. #endif
  372.     return isdigit(*string) ? atoi(string) : 0;
  373. }
  374.  
  375. /* 
  376.  * nntp_ask_server:  ask the server a question and return the answer.
  377.  *
  378.  *    Expects to be connected to the server.  
  379.  *    Returns the numerical value of the reponse, or -1 in case of
  380.  *    errors.
  381.  *    Contains some code to handle server timeouts intelligently.
  382.  */
  383.  
  384. nntp_ask_server(string, size)
  385.     char *string;
  386.     int size;
  387. {
  388.     int response;
  389.  
  390.     if (nntp_put_server(string) < 0)
  391.     return -1;
  392.     response = nntp_get_server(string, size);
  393.  
  394.     /* 
  395.      * Handle the response from the server.  Responses are handled as
  396.      * followes:
  397.      *
  398.      * 100-199    Informational.  Passed back. (should they be ignored?).
  399.      * 200-299    Ok messages.  Passed back.
  400.      * 300-399    Ok and proceed.  Can not happen in nn.
  401.      * 400-499    Errors (no article, etc).  Passed up and handled there.
  402.      * 500-599    Fatal NNTP errors.  Handled below.
  403.      */
  404.     if (response == ERR_GOODBYE || response > ERR_COMMAND) {
  405.     nntp_failed = 1;
  406.     nntp_close_server();
  407.  
  408.     if (response != ERR_TIMEOUT) {    /* if not timeout, complain */
  409.         if (is_master)
  410.         sys_error("NNTP botch: internal error: %s", string);
  411.         else
  412.         user_error("NNTP botch: internal error: %s", string);
  413.         /* NOTREACHED */
  414.     }
  415.     try_again = 1;
  416.     is_set = 0;
  417.     }
  418.     return response;
  419. }
  420.  
  421. /* 
  422.  * nntp_close_server: close the connection to the server.
  423.  */
  424.  
  425. nntp_close_server()
  426. {
  427.     if (!is_connected)
  428.     return;
  429.     
  430.     if (!nntp_failed) {            /* avoid infinite recursion */
  431.     char line[NNTP_STRLEN];
  432.     int n;
  433.     
  434.     strcpy(line, "QUIT");
  435.     n = nntp_ask_server(line, sizeof line);
  436.     if (n != OK_GOODBYE)
  437.         ;                /* WHAT NOW ??? */
  438.     }
  439.     
  440.     (void) fclose(nntp_out);
  441.     (void) fclose(nntp_in);
  442.     
  443.     is_connected = 0;
  444. }
  445.  
  446. /* 
  447.  * nntp_io_error: signal an I/O error in talking to the server.
  448.  *
  449.  *     An nn client terminates a session with the user.  The master
  450.  *    simply closes the connection.  The flag nntp_failed is set, for
  451.  *    use by the master to terminate collection.
  452.  *
  453.  *    BUG: if the nntp server is forcibly killed, errno can contain a
  454.  *    bogus value, resulting in strange error messages.  It is
  455.  *    probably better just to write out the numerical value of errno.
  456.  */
  457.  
  458. nntp_io_error()
  459. {
  460.     nntp_failed = 1;
  461.     if (is_master) {
  462.     if (is_connected) {
  463.         log_entry('N', "Lost connection to server %s: %s", nntp_server, syserr());
  464.         nntp_close_server();
  465.     }
  466.     } else {
  467.     user_error("Lost connection to NNTP server %s: %s", nntp_server, syserr());
  468.         /* NOTREACHED */
  469.     }
  470. }
  471.  
  472. /* 
  473.  * nntp_no_post: Check to see whether posting is allowed.
  474.  */
  475.  
  476. nntp_no_post()
  477. {
  478.     if (!is_connected && nntp_server_init() < 0)
  479.     return 1;            /* If we cannot connect, neither can inews */
  480.     if (can_post == 0) {
  481.     msg("NNTP server does not allow postings from this host.  Sorry!");
  482.     return 1;
  483.     }
  484.     return 0;
  485. }
  486.    
  487.  
  488. /* 
  489.  * nntp_copy_text: copy text response into file.
  490.  *
  491.  *     Sends COMMAND to the server, and copies the following text response
  492.  *     into an open file.
  493.  *    Return -1 on error, 0 otherwise.  It is treated as an error, if
  494.  *    the returned response it not what was expected.
  495.  */
  496.  
  497. nntp_copy_text(fp, command, expected)
  498.     FILE *fp;
  499.     char *command;
  500.     int expected;
  501. {
  502.     char buf[NNTP_STRLEN];
  503.     char *cp;
  504.     int n, nlines;
  505.     
  506.     strcpy(buf, command);
  507.     if ((n = nntp_ask_server(buf, sizeof buf)) < 0) {
  508.     return -1;
  509.     }
  510.     if (n != expected)
  511.     return -1;
  512.     
  513.     nlines = 0;
  514.     while ((n = nntp_get_server_line(buf, sizeof buf)) >= 0) {
  515.     cp = buf;
  516.     if (*cp == '.')
  517.         if (*++cp == '\0')
  518.         return nlines > 0 ? 0 : -1;
  519.     fputs(cp, fp);
  520.     putc('\n', fp);
  521.     nlines++;
  522.     }
  523.     return -1;
  524. }
  525.  
  526. /* 
  527.  * nntp_get_active:  get a copy of the active file.
  528.  *
  529.  *     If we are the master get a copy of the file from the nntp server.
  530.  *     nnadmin just uses the one we already got.  In this way the master 
  531.  *    can maintain a remote copy of the servers active file.  
  532.  *    We try to be a little smart, if not inefficient, about the 
  533.  *    modification times on the local active file.
  534.  *    Even when the master is running on the nntp server, a separate
  535.  *    copy of the active file will be made for access via NFS.
  536.  */
  537.  
  538. nntp_get_active()
  539. {
  540.     FILE *old, *new;
  541.     char bufo[NNTP_STRLEN], bufn[NNTP_STRLEN];
  542.     char *new_name;
  543.     int same;
  544.     
  545.     if (!is_master) 
  546.     return access(news_active, 4);
  547.  
  548.  again:
  549.     if (!is_connected && nntp_server_init() < 0)
  550.     return -1;
  551.     
  552.     new_name = mktemp(relative(db_directory, ".actXXXXXX"));
  553.     
  554.     if ((new = fopen(new_name, "w+")) == NULL)
  555.     return -1;
  556.     if (nntp_copy_text(new, "LIST", OK_GROUPS) < 0) {
  557.     fclose(new);
  558.     unlink(new_name);
  559.     if (try_again)            /* Handle nntp server timeouts */
  560.         goto again;
  561.     else
  562.         return -1;
  563.     }
  564.     rewind(new);
  565.     same = 0;
  566.     if ((old = open_file(news_active, OPEN_READ)) != NULL) {
  567.     do {
  568.         fgets(bufo, sizeof bufo, old);
  569.         fgets(bufn, sizeof bufn, new);
  570.     } while (!feof(old) && !feof(new) && strcmp(bufo, bufn) == 0);
  571.     same = feof(old) && feof(new);
  572.     fclose(old);
  573.     }
  574.     fclose(new);
  575.     
  576.     if (same) 
  577.     unlink(new_name);
  578.     else
  579.     if (rename(new_name, news_active) != 0)
  580.         sys_error("Cannot rename %s to %s", new_name, news_active);
  581.     
  582.     return 0;
  583. }
  584.  
  585. /* 
  586.  * The following functions implements a simple lru cache of recently
  587.  * accessed articles.  It is a simple way to improve effeciency.  Files
  588.  * must be kept by name, because the rest of the code expects to be able
  589.  * to open an article multiple times, and get separate file pointers.
  590.  */
  591.  
  592. struct cache {
  593.     char        *file_name;    /* file name */
  594.     article_number    art;        /* article stored in file */
  595.     group_header    *grp;        /* from this group */
  596.     unsigned        time;        /* time last accessed */
  597. } cache[NNTPCACHE];
  598.  
  599. static unsigned cur_time = 1;        /* virtual time */
  600.  
  601. /* 
  602.  * nntp_search_cache: search the cache for an (article, group) pair.
  603.  *
  604.  *     Returns a pointer to the slot where it is, null otherwise 
  605.  */
  606.  
  607. struct cache *nntp_search_cache(art, gh)
  608.     article_number art;
  609.     group_header *gh;
  610. {
  611.     struct cache *cptr = cache;
  612.     int i;
  613.     
  614.     for (i = 0; i < NNTPCACHE; i++, cptr++)
  615.     if (cptr->art == art && cptr->grp == gh) {
  616.         cptr->time = cur_time++;
  617.         return cptr;
  618.     }
  619.     return NULL;
  620. }
  621.  
  622. /* 
  623.  * nntp_new_slot: get a free cache slot.
  624.  *
  625.  *     Returns a pointer to the allocated slot.  
  626.  *     Frees the old filename, and allocates a new, unused filename.
  627.  *    The user's files are in ~/.nn, and the master's are in LIB_DIRECTORY.
  628.  */
  629.  
  630. struct cache *nntp_new_slot()
  631. {
  632.     struct cache *cptr = cache;
  633.     int i, lru;
  634.     unsigned min_time = cur_time;
  635.     char name[24];
  636.     
  637.     for (i = 0; i < NNTPCACHE; i++, cptr++)
  638.     if (min_time > cptr->time) {
  639.         min_time = cptr->time;
  640.         lru = i;
  641.     }
  642.     cptr = &cache[lru];
  643.     
  644.     if (cptr->file_name) {
  645.     unlink(cptr->file_name);
  646.     free(cptr->file_name);
  647.     }
  648.     sprintf(name, "nn%02d-XXXXXX", lru);
  649.     cptr->file_name =
  650.     copy_str(relative(is_master ? lib_directory : nn_directory,
  651.               mktemp(name)));
  652.     cptr->time = cur_time++;
  653.     return cptr;
  654. }
  655.  
  656. /* 
  657.  * nntp_clean_cache: clean up the cache.
  658.  *
  659.  *     Removes all allocated files.
  660.  */
  661.  
  662. void nntp_clean_cache()
  663. {
  664.     struct cache *cptr = cache;
  665.     int i;
  666.     
  667.     for (i = 0; i < NNTPCACHE; i++, cptr++)
  668.     if (cptr->file_name)
  669.         unlink(cptr->file_name);
  670. }
  671.  
  672. /* 
  673.  * nntp_set_group: set the server's current group.
  674.  *
  675.  *     Actual communication is delayed until an article is accessed, to
  676.  *     avoid unnecessary traffic.
  677.  */
  678.  
  679. nntp_set_group(gh)
  680.     group_header *gh;
  681. {
  682.     group_hd = gh;
  683.     is_set = 0;
  684.     return 0;
  685. }
  686.  
  687. /* 
  688.  * nntp_get_article: get an article from the server.
  689.  *
  690.  *     Returns a FILE pointer.
  691.  *    If necessary the server's current group is set.
  692.  *    The article (header and body) are copied into a file, so they
  693.  *    are seekable (nn likes that).
  694.  */
  695.  
  696. FILE *nntp_get_article(article)
  697.     article_number article;
  698. {
  699.     char buf[NNTP_STRLEN];
  700.     FILE *tmp;
  701.     struct cache *cptr;
  702.     int n;
  703.  
  704.  again:
  705.     if (!is_connected && nntp_server_init() < 0) {
  706.     return NULL;
  707.     }
  708.     
  709.     /* 
  710.      * Set the server group to the current group
  711.      */
  712.     if (is_set == 0) {
  713.     sprintf(buf, "GROUP %s", group_hd->group_name);
  714.     if ((n = nntp_ask_server(buf, sizeof buf)) < 0) {
  715.         return NULL;
  716.     }
  717.     if (n != OK_GROUP) {
  718.         if (try_again)
  719.         goto again;        /* Handle nntp server timeouts */
  720.         else
  721.         return NULL;
  722.     }
  723.     is_set = 1;
  724.     }
  725.     /* 
  726.      * Search the cache for the requested article, and allocate a new
  727.      * slot if necessary.
  728.      */
  729.     cptr = nntp_search_cache(article, group_hd);
  730.     if (cptr != 0) {
  731.     return open_file(cptr->file_name, OPEN_READ);
  732.     } 
  733.     cptr = nntp_new_slot();
  734.     
  735.     if ((tmp = open_file(cptr->file_name, OPEN_CREATE)) == NULL)
  736.     return NULL;
  737.     
  738.     /* 
  739.      * Copy the article.
  740.      */
  741.     sprintf(buf, "ARTICLE %d", article);
  742.     if (nntp_copy_text(tmp, buf, OK_ARTICLE) < 0) {
  743.     fclose(tmp);
  744.     if (try_again)
  745.         goto again;            /* Handle nntp server timeouts */
  746.     else
  747.         return NULL;
  748.     }
  749.     fclose(tmp);
  750.     if ((tmp = open_file(cptr->file_name, OPEN_READ)) != NULL) {
  751.     cptr->art = article;
  752.     cptr->grp = group_hd;
  753.     }
  754.     return tmp;
  755. }
  756.  
  757. /* 
  758.  * nntp_cleanup:  clean up after an nntp session.
  759.  *
  760.  *    Called from nn_exit().
  761.  */
  762.  
  763. nntp_cleanup()
  764. {
  765.     if (is_connected)
  766.     nntp_close_server();
  767.     nntp_clean_cache();
  768. }
  769. #endif /* NNTP */
  770.  
  771.